<html>
  <head>
    <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>JavaScript核心知识 | 少数派报告</title>
<meta name="description" content="Minority Report" />
<link rel="shortcut icon" href="https://www.timegarage.works/favicon.ico?v=1598261443340">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.10.0/katex.min.css">
<link rel="stylesheet" href="https://www.timegarage.works/styles/main.css">

<script src="https://cdn.bootcss.com/highlight.js/9.12.0/highlight.min.js"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Droid+Serif:400,700">



  </head>
  <body>
    <div class="main">
      <div class="main-content">
        <div class="site-header">
  <a href="https://www.timegarage.works">
  <img class="avatar" src="https://www.timegarage.works/images/avatar.png?v=1598261443340" alt="">
  </a>
  <h1 class="site-title">
    少数派报告
  </h1>
  <p class="site-description">
    Minority Report
  </p>
  <div class="menu-container">
    
      
        <a href="/" class="menu">
          首页
        </a>
      
    
      
        <a href="/archives" class="menu">
          归档
        </a>
      
    
      
        <a href="/tags" class="menu">
          标签
        </a>
      
    
      
        <a href="/post/Portfolio/" class="menu">
          作品
        </a>
      
    
      
        <a href="/post/about/" class="menu">
          关于
        </a>
      
    
  </div>
  <div class="social-container">
    
      
    
      
    
      
    
      
    
      
    
  </div>
</div>

        <div class="post-detail">
          <article class="post">
            <h2 class="post-title">
              JavaScript核心知识
            </h2>
            <div class="post-info">
              <span>
                2020-07-26
              </span>
              <span>
                19 min read
              </span>
              
                <a href="https://www.timegarage.works/tag/2M4H636eY/" class="post-tag">
                  # JavaScript
                </a>
              
                <a href="https://www.timegarage.works/tag/FrontEnd/" class="post-tag">
                  # 前端
                </a>
              
            </div>
            
              <img class="post-feature-image" src="https://www.timegarage.works/post-images/JavaScript.jpg" alt="">
            
            <div class="post-content-wrapper">
              <div class="post-content">
                <center>本文参考了Mozilla的JavaScript教程，旨在对JavaScript核心知识进行梳理。</center>
<!-- more -->
<h1 id="1-基础概念">1 基础概念</h1>
<h2 id="11-简介">1.1 简介</h2>
<p>JavaScript 是一种多范式的动态语言，它包含类型、运算符、标准内置（ built-in）对象和方法。它的语法来源于 Java 和 C，所以这两种语言的许多语法特性同样适用于 JavaScript。JavaScript 通过原型链而不是类来支持面向对象编程。JavaScript同样支持函数式编程——因为它们也是对象，函数也可以被保存在变量中，并且像其他对象一样被传递。</p>
<h2 id="12-类型">1.2 类型</h2>
<p>JavaScript支持多种数据类型，包括Numer、String、Boolean、Object（包括Function、Array、Date、RegExp）、Symbol（ES2015新增）、null、undefined。</p>
<h3 id="121-数值">1.2.1 数值</h3>
<p>根据语言规范，JavaScript 采用“遵循 IEEE 754 标准的双精度 64 位格式”（&quot;double-precision 64-bit format IEEE 754 values&quot;）表示数字。——在JavaScript（除了<code>BigInt</code>）中，<strong>并不存在整数/整型(Integer)。</strong></p>
<pre><code class="language-javascript">0.1 + 0.2 = 0.30000000000000004;
</code></pre>
<p>JavaScript支持标准运算符和内置对象Math。单元运算符+可以把数字字符串转换为数值，此外内置函数parseInt(string, base)也可将字符串转换为整型。若字符串无法转换为数值类型，则会返回<code>NaN</code>。使用内置函数<code>isNaN()</code>可判断变量是否为<code>NaN</code>。</p>
<p>注：<code>parseInt()</code>和<code>parseFloat()</code>函数会尝试逐个解析字符串中的字符，直到遇上一个无法被解析成数字的字符，然后返回该字符前所有数字字符组成的数字。但是运算符 &quot;+&quot;对字符串的转换方式与之不同， 只要字符串含有无法被解析成数字的字符，该字符串就将被转换成 <code>NaN</code>。</p>
<pre><code class="language-javascript">parseFloat(&quot;10.2abc&quot;); // 10.2
+ &quot;10.2abc&quot;; // NaN
</code></pre>
<p>JavaScript有两个特殊值<code>Infinity</code>（正无穷）和<code>-Infinity</code>（负无穷），可以使用内置函数<code>isFinite()</code>来判断一个变量是否是一个有穷数。</p>
<pre><code class="language-javascript">isFinite(1/0); // false
isFinite(Infinity); // false
isFinite(-Infinity); // false
isFinite(NaN); // false

isFinite(0); // true
isFinite(2e64); // true

isFinite(&quot;0&quot;); // true
// 如果是纯数值类型的检测，则返回 false：
Number.isFinite(&quot;0&quot;); // false
</code></pre>
<h3 id="122-字符串">1.2.2 字符串</h3>
<p>JavaScript 中的字符串是一串Unicode 字符序列。更准确地说，它们是一串UTF-16编码单元的序列，每一个编码单元由一个 16 位二进制数表示。每一个Unicode字符由一个或两个编码单元来表示。</p>
<p>访问字符串<code>length</code>属性可以方便地获得字符串长度。使用字符串是一种JavaScript对象，因此可以使用方法操作字符串。</p>
<pre><code class="language-javascript">&quot;hello&quot;.length; // 5
&quot;hello&quot;.charAt(0); // &quot;h&quot;
&quot;hello, world&quot;.replace(&quot;world&quot;, &quot;mars&quot;); // &quot;hello, mars&quot;
&quot;hello&quot;.toUpperCase(); // &quot;HELLO&quot;
</code></pre>
<h3 id="123-其他类型">1.2.3 其他类型</h3>
<p>与其他类型不同，JavaScript 中的 <code>null</code> 表示一个空值（non-value），必须使用 null 关键字才能访问，<code>undefined</code> 是一个“undefined（未定义）”类型的对象，表示一个未初始化的值，也就是还没有被分配的值。我们之后再具体讨论变量，但有一点可以先简单说明一下，JavaScript 允许声明变量但不对其赋值，一个未被赋值的变量就是 <code>undefined</code> 类型。还有一点需要说明的是，<code>undefined</code> 实际上是一个不允许修改的常量。</p>
<p>JavaScript 包含布尔类型，这个类型的变量有两个可能的值，分别是 <code>true</code> 和 <code>false</code>（两者都是关键字）。根据具体需要，JavaScript 按照如下规则将变量转换成布尔类型（隐式转换）：</p>
<ol>
<li><code>false</code>、<code>0</code>、空字符串（<code>&quot;&quot;</code>）、<code>NaN</code>、<code>null</code> 和 <code>undefined</code> 被转换为 <code>false</code></li>
<li>所有其他值被转换为 <code>true</code></li>
</ol>
<p>使用Boolean()函数可完成显式转换：</p>
<pre><code class="language-javascript">Boolean(''); // false
Boolean(234); // true
</code></pre>
<p>JavaScript 支持包括 <code>&amp;&amp;</code>（逻辑与）、<code>||</code> （逻辑或）和<code>!</code>（逻辑非）在内的一些逻辑运算符。</p>
<h2 id="13-变量">1.3 变量</h2>
<p>在 JavaScript 中声明一个新变量的方法是使用关键字 <code>let</code> 、<code>const</code> 和 <code>var</code>。</p>
<p><code>let</code> 语句声明一个块级作用域的本地变量，并且可选的将其初始化为一个值。</p>
<p><code>const</code> 允许声明一个不可变的常量。这个常量在定义域内总是可见的。</p>
<p><code>var</code> 是最常见的声明变量的关键字。它没有其他两个关键字的种种限制。这是因为它是传统上在 JavaScript 声明变量的唯一方法。使用 <strong><code>var</code></strong> 声明的变量在它所声明的整个函数都是可见的。</p>
<p><b>JavaScript 与其他语言的（如 Java）的重要区别是在 JavaScript 中语句块（blocks）是没有作用域的，只有函数有作用域。</b>因此如果在一个复合语句中（如 if 控制结构中）使用 var 声明一个变量，那么它的作用域是整个函数（复合语句在函数中）。 但是从 ES6 开始情况将有所不同， <code>let</code> 和 <code>const</code> 关键字允许你创建块作用域的变量。</p>
<h2 id="14-运算符">1.4 运算符</h2>
<p>JavaScript的算术操作符包括 <code>+</code>、<code>-</code>、<code>*</code>、<code>/</code> 和 <code>%</code> ——求余（与模运算相同）。赋值使用 <code>=</code> 运算符，此外还有一些复合运算符，如 <code>+=</code> 和 <code>-=</code>，它们等价于 <code>x = x *operator* y</code>。</p>
<p>可以使用 <code>++</code> 和 <code>--</code> 分别实现变量的自增和自减。两者都可以作为前缀或后缀操作符使用。</p>
<p><code>+</code> 操作符还可以用来连接字符串，如果你用一个字符串加上一个数字（或其他值），那么操作数都会被首先转换为字符串。通过与空字符串相加，可以将某个变量快速转换成字符串类型。</p>
<pre><code class="language-javascript">&quot;hello&quot; + &quot; world&quot;; // hello world
&quot;3&quot; + 4 + 5; // 345
3 + 4 + &quot;5&quot;; // 75
&quot;&quot; + 12; // &quot;12&quot;
</code></pre>
<p>JavaScript 中的<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Comparison_Operators">比较操作</a>使用 <code>&lt;</code>、<code>&gt;</code>、<code>&lt;=</code> 和 <code>&gt;=</code>，这些运算符对于数字和字符串都通用（可以直接用字符串和数值类型比较大小）。相等的比较稍微复杂一些,<b>由两个“<code>=</code>（等号）”组成的相等运算符有类型自适应的功能。如果在比较前不需要自动类型转换，应该使用由三个<code>=</code>（等号）组成的相等运算符。</b></p>
<pre><code class="language-javascript">123 == &quot;123&quot; // true
1 == true; // true
1 === true; //false
123 === &quot;123&quot;; // false
</code></pre>
<p>JavaScript 还支持 <code>!=</code> 和 <code>!==</code> 两种不等运算符，具体区别与两种相等运算符的区别类似。</p>
<p>JavaScript 还提供了位操作符。</p>
<h2 id="15-控制结构">1.5 控制结构</h2>
<p>JavaScript 的控制结构与其他类 C 语言类似，可以使用 <code>if</code> 和 <code>else</code> 来定义条件语句。JavaScript 支持 <code>while</code> 循环和 <code>do-while</code> 循环。前者适合常见的基本循环操作，如果需要循环体至少被执行一次则可以使用 <code>do-while</code>。JavaScript 的 <code>for</code> 循环与 C 和 Java 中的相同：使用时可以在一行代码中提供控制信息。</p>
<p>JavaScript 也还包括其他两种重要的 for 循环： <code>for...of</code>和<code>for...in</code></p>
<pre><code class="language-javascript">for (let value of array) {
  // do something with value
}
for (let property in object) {
  // do something with object property
}
</code></pre>
<p><code>&amp;&amp;</code> 和 <code>||</code> 运算符使用短路逻辑（short-circuit logic），是否会执行第二个语句（操作数）取决于第一个操作数的结果。在需要访问某个对象的属性时，使用这个特性可以事先检测该对象是否为空，或用于缓存值（当错误值无效时）。</p>
<pre><code class="language-javascript">var name = o &amp;&amp; o.getName();
var name = cachedName || (cachedName = getName());
</code></pre>
<p>JavaScript支持用于条件表达式的三元操作符。</p>
<pre><code class="language-javascript">var allowed = (age &gt; 18) ? &quot;yes&quot; : &quot;no&quot;;
</code></pre>
<p>在需要多重分支时可以使用 <code>基于一个数字或字符串的switch</code> 语句。如果你不使用 <code>break</code> 语句，JavaScript 解释器将会执行之后 <code>case</code> 中的代码。除非是为了调试，一般你并不需要这个特性，所以大多数时候不要忘了加上 <code>break。``default</code> 语句是可选的。<code>switch</code> 和 <code>case</code> 都可以使用需要运算才能得到结果的表达式；在 <code>switch</code> 的表达式和 <code>case</code> 的表达式是使用 <code>===</code> 严格相等运算符进行比较的。</p>
<pre><code class="language-javascript">switch(action) {
    case 'draw':
        drawIt();
        break;
    case 'eat':
        eatIt();
        break;
    default:
        doNothing();
}
</code></pre>
<h2 id="16-对象">1.6 对象</h2>
<p>JavaScript 中的对象，Object，可以简单理解成“名称-值”对（而不是键值对：现在，ES 2015 的映射表（Map），比对象更接近键值对），不难联想 JavaScript 中的对象与下面这些概念类似：</p>
<ul>
<li>Python 中的字典（Dictionary）</li>
<li>Perl 和 Ruby 中的散列/哈希（Hash）</li>
<li>C/C++ 中的散列表（Hash table）</li>
<li>Java 中的散列映射表（HashMap）</li>
<li>PHP 中的关联数组（Associative array）</li>
</ul>
<p>有两种简单方法可以创建一个空对象：</p>
<pre><code class="language-javascript">var obj = new Object();
var obj = {};
</code></pre>
<p>这两种方法在语义上是相同的。第二种更方便的方法叫作“对象字面量（object literal）”法。这种也是 JSON 格式的核心语法，一般我们优先选择第二种方法。</p>
<p>“对象字面量”也可以用来在对象实例中定义一个对象：</p>
<pre><code class="language-js">var obj = {
    name: &quot;Carrot&quot;,
    for: &quot;Max&quot;,//'for' 是保留字之一，使用'_for'代替
    details: {
        color: &quot;orange&quot;,
        size: 12
    }
}
</code></pre>
<p>对象的属性可以通过链式（chain）表示方法进行访问：</p>
<pre><code class="language-js">obj.details.color; // orange
obj[&quot;details&quot;][&quot;size&quot;]; // 12
</code></pre>
<p>下面的例子创建了一个对象原型，<code>Person</code>，和这个原型的实例，<strong><code>You</code></strong>。</p>
<pre><code class="language-js">function Person(name, age) {
  this.name = name;
  this.age = age;
}

// 定义一个对象
var You = new Person(&quot;You&quot;, 24); 
// 我们创建了一个新的 Person，名称是 &quot;You&quot; 
// (&quot;You&quot; 是第一个参数, 24 是第二个参数..)
</code></pre>
<p>完成创建后，对象属性可以通过如下两种方式进行赋值和访问：</p>
<pre><code class="language-js">obj.name = &quot;Simon&quot;
var name = obj.name;
</code></pre>
<p>和：</p>
<pre><code class="language-js">// bracket notation
obj['name'] = 'Simon';
var name = obj['name'];
// can use a variable to define a key
var user = prompt('what is your key?')
obj[user] = prompt('what is its value?')
</code></pre>
<p><b>这两种方法在语义上也是相同的。第二种方法的优点在于属性的名称被看作一个字符串，这就意味着它可以在运行时被计算，缺点在于这样的代码有可能无法在后期被解释器优化。</b></p>
<h2 id="17-数组">1.7 数组</h2>
<p>JavaScript 中的数组是一种特殊的对象。它的工作原理与普通对象类似（以数字为属性名，但只能通过<code>[]</code> 来访问），但数组还有一个特殊的属性——<code>length</code>（长度）属性。这个属性的值通常比数组最大索引大 1。</p>
<p>可以通过如下方式遍历一个数组：</p>
<pre><code class="language-js">for (var i = 0; i &lt; a.length; i++) {
    // Do something with a[i]
}
</code></pre>
<p>ES2015 引入了更加简洁的 <code>for...of</code>循环，可以用它来遍历可迭代对象，例如数组：</p>
<pre><code class="language-js">for (const currentValue of a) {
  // Do something with currentValue
}
</code></pre>
<p>遍历数组的另一种方法是使用 <code>for...in</code>循环， 然而这并不是遍历数组元素而是数组的索引。注意，如果哪个家伙直接向 <code>Array.prototype</code> 添加了新的属性，使用这样的循环这些属性也同样会被遍历。所以并不推荐使用这种方法遍历数组：</p>
<pre><code class="language-js">for (var i in a) {
  // Do something with a[i]
}
</code></pre>
<p>ECMAScript 5 增加了另一个遍历数组的方法，<code>forEach()</code>：</p>
<pre><code class="language-js">[&quot;dog&quot;, &quot;cat&quot;, &quot;hen&quot;].forEach(function(currentValue, index, array) {
  // Do something with currentValue or array[index]
});
</code></pre>
<p>Array（数组）类还自带了许多方法。建议查看 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array">Array 方法的完整文档</a>。</p>
<table>
<thead>
<tr>
<th style="text-align:left">方法名称</th>
<th style="text-align:left">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><code>a.toString()</code></td>
<td style="text-align:left">返回一个包含数组中所有元素的字符串，每个元素通过逗号分隔。</td>
</tr>
<tr>
<td style="text-align:left"><code>a.toLocaleString()</code></td>
<td style="text-align:left">根据宿主环境的区域设置，返回一个包含数组中所有元素的字符串，每个元素通过逗号分隔。</td>
</tr>
<tr>
<td style="text-align:left"><code>a.concat(item1[, item2[, ...[, itemN]]])</code></td>
<td style="text-align:left">返回一个数组，这个数组包含原先 <code>a</code> 和 <code>item1、item2、……、itemN</code> 中的所有元素。</td>
</tr>
<tr>
<td style="text-align:left"><code>a.join(sep)</code></td>
<td style="text-align:left">返回一个包含数组中所有元素的字符串，每个元素通过指定的 <code>sep</code> 分隔。</td>
</tr>
<tr>
<td style="text-align:left"><code>a.pop()</code></td>
<td style="text-align:left">删除并返回数组中的最后一个元素。</td>
</tr>
<tr>
<td style="text-align:left"><code>a.push(item1, ..., itemN)</code></td>
<td style="text-align:left">将 <code>item1、item2、……、itemN</code> 追加至数组 <code>a</code>。</td>
</tr>
<tr>
<td style="text-align:left"><code>a.reverse()</code></td>
<td style="text-align:left">数组逆序（会更改原数组 <code>a</code>）。</td>
</tr>
<tr>
<td style="text-align:left"><code>a.shift()</code></td>
<td style="text-align:left">删除并返回数组中第一个元素。</td>
</tr>
<tr>
<td style="text-align:left"><code>a.slice(start, end)</code></td>
<td style="text-align:left">返回子数组，以 <code>a[start]</code> 开头，以 <code>a[end]</code>前一个元素结尾。</td>
</tr>
<tr>
<td style="text-align:left"><code>a.sort([cmpfn])</code></td>
<td style="text-align:left">依据可选的比较函数 <code>cmpfn</code> 进行排序，如果未指定比较函数，则按字符顺序比较（即使被比较元素是数字）。</td>
</tr>
<tr>
<td style="text-align:left"><code>a.splice(start, delcount[, item1[, ...[, itemN]]])</code></td>
<td style="text-align:left">从 <code>start</code> 开始，删除 <code>delcount</code> 个元素，然后插入所有的 <code>item</code>。</td>
</tr>
<tr>
<td style="text-align:left"><code>a.unshift(item1[, item2[, ...[, itemN]]])</code></td>
<td style="text-align:left">将 <code>item</code> 插入数组头部，返回数组新长度（考虑 <code>undefined</code>）。</td>
</tr>
</tbody>
</table>
<h2 id="18-函数">1.8 函数</h2>
<p>一个 JavaScript 函数可以包含 0 个或多个已命名的变量。函数体中的表达式数量也没有限制。你可以声明函数自己的局部变量。<code>return</code> 语句在返回一个值并结束函数。如果没有使用 <code>return</code> 语句，或者一个没有值的 <code>return</code> 语句，JavaScript 会返回 <code>undefined</code>。已命名的参数更像是一个指示而没有其他作用。</p>
<p>如果调用函数时没有提供足够的参数，缺少的参数会被 <code>undefined</code> 替代。函数实际上是访问了函数体中一个名为 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments"><code>arguments</code></a> 的内部对象，这个对象就如同一个类似于数组的对象一样，包括了所有被传入的参数。</p>
<pre><code class="language-javascript">function add() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i &lt; j; i++) {
        sum += arguments[i];
    }
    return sum;
}

add(2, 3, 4, 5); // 14
</code></pre>
<p>为了使代码变短一些，我们可以使用<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Rest_parameters">剩余参数</a>来替换arguments的使用。在这方法中，我们可以传递任意数量的参数到函数中同时尽量减少我们的代码。这个<strong>剩余参数操作符</strong>在函数中以：<strong>...variable</strong> 的形式被使用，它将包含在调用函数时使用的未捕获整个参数列表到这个变量中。我们同样也可以将 <strong>for</strong> 循环替换为 <strong>for...of</strong> 循环来返回我们变量的值。</p>
<pre><code class="language-javascript">function avg(...args) {
  var sum = 0;
  for (let value of args) {
    sum += value;
  }
  return sum / args.length;
}

avg(2, 3, 4, 5); // 3.5
</code></pre>
<p>JavaScript 允许任意函数对象的 <code>apply()</code> 方法来传递给它一个数组作为参数列表。</p>
<pre><code class="language-js">avg.apply(null, [2, 3, 4, 5]); // 3.5
</code></pre>
<p>传给 <code>apply()</code> 的第二个参数是一个数组，它将被当作 <code>avg()</code> 的参数列表使用，至于第一个参数 <code>null</code>，我们将在后面讨论。</p>
<p>JavaScript 允许创建匿名函数：</p>
<pre><code class="language-javascript">var avg = function() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i &lt; j; i++) {
        sum += arguments[i];
    }
    return sum / arguments.length;
};
</code></pre>
<p>JavaScript 允许你命名函数表达式，你可以命名立即调用的函数表达式（IIFE——Immediately Invoked Function Expression）。</p>
<h2 id="19-自定义对象">1.9 自定义对象</h2>
<p>在经典的面向对象语言中，对象是指数据和在这些数据上进行的操作的集合。与 C++ 和 Java 不同，JavaScript 是一种基于原型的编程语言，并没有 class 语句，而是把函数用作类。那么让我们来定义一个人名对象，这个对象包括人的姓和名两个域（field）。名字的表示有两种方法：“名 姓（First Last）”或“姓, 名（Last, First）”。使用我们前面讨论过的函数和对象概念，可以像这样完成定义：</p>
<pre><code class="language-js">function makePerson(first, last) {
    return {
        first: first,
        last: last,
        fullName: function() {
            return this.first + ' ' + this.last;
        },
        fullNameReversed: function() {
            return this.last + ', ' + this.first;
        }
    }
}
s = makePerson(&quot;Simon&quot;, &quot;Willison&quot;);
s.fullName(); // Simon Willison
s.fullNameReversed(); // Willison, Simon
</code></pre>
<h2 id="110-内部函数">1.10 内部函数</h2>
<p>JavaScript 允许在一个函数内部定义函数，这一点我们在之前的 <code>makePerson()</code> 例子中也见过。关于 JavaScript 中的嵌套函数，一个很重要的细节是，它们可以访问父函数作用域中的变量：</p>
<pre><code class="language-js">function parentFunc() {
  var a = 1;

  function nestedFunc() {
    var b = 4; // parentFunc 无法访问 b
    return a + b;
  }
  return nestedFunc(); // 5
}
</code></pre>
<p>如果某个函数依赖于其他的一两个函数，而这一两个函数对你其余的代码没有用处，你可以将它们嵌套在会被调用的那个函数内部，这样做可以减少全局作用域下的函数的数量，这有利于编写易于维护的代码。</p>
<h2 id="111-闭包">1.11 闭包</h2>
<p>每当 JavaScript 执行一个函数时，都会创建一个作用域对象（scope object），用来保存在这个函数中创建的局部变量。它使用一切被传入函数的变量进行初始化（初始化后，它包含一切被传入函数的变量）。作用域对象组成了一个名为作用域链（scope chain）的（调用）链。它和 JavaScript 的对象系统使用的原型（prototype）链相类似。</p>
<p>一个<strong>闭包</strong>，就是 一个函数与其被创建时所带有的作用域对象的组合。闭包允许你保存状态——所以，它们可以用来代替对象。</p>
<h1 id="2-es6特性">2 ES6特性</h1>
<h2 id="21-箭头函数">2.1 箭头函数</h2>
<p>箭头函数表达式的语法比函数表达式更简洁，并且没有自己的this，arguments，super或new.target。箭头函数表达式更适用于那些本来需要匿名函数的地方，并且它不能用作构造函数。</p>
<p><b>基础语法</b></p>
<pre><code class="language-javascript">(param1, param2, …, paramN) =&gt; { statements } 
(param1, param2, …, paramN) =&gt; expression
//相当于：(param1, param2, …, paramN) =&gt;{ return expression; }

// 当只有一个参数时，圆括号是可选的：
(singleParam) =&gt; { statements }
singleParam =&gt; { statements }

// 没有参数的函数应该写成一对圆括号。
() =&gt; { statements }
</code></pre>
<p><b>高级语法</b></p>
<pre><code class="language-javascript">//加括号的函数体返回对象字面量表达式：
params =&gt; ({foo: bar})

//支持剩余参数和默认参数
(param1, param2, ...rest) =&gt; { statements }
(param1 = defaultValue1, param2, …, paramN = defaultValueN) =&gt; { 
statements }

//同样支持参数列表解构
let f = ([a, b] = [1, 2], {x: c} = {x: a + b}) =&gt; a + b + c;
f();  // 6
</code></pre>
<h2 id="22-class">2.2 class</h2>
<p>类实际上是个“特殊的函数”，就像你能够定义的函数表达式和函数声明一样，类语法有两个组成部分：类表达式和类声明。</p>
<p>定义一个类的一种方法是使用一个<strong>类声明</strong>。要声明一个类，你可以使用带有<code>class</code>关键字的类名（这里是“Rectangle”）。</p>
<pre><code class="language-js">class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}
</code></pre>
<p>函数声明和类声明之间的一个重要区别是函数声明会提升，类声明不会。你首先需要声明你的类，然后访问它，否则像下面的代码会抛出一个<code>ReferenceError</code>：</p>
<pre><code class="language-js">let p = new Rectangle(); // ReferenceError
class Rectangle {}
</code></pre>
<p><code>extends</code>关键字在类声明或类表达式中用于创建一个类作为另一个类的一个子类。</p>
<pre><code class="language-js">class Animal { 
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name); // 调用超类构造函数并传入name参数
  }

  speak() {
    console.log(`${this.name} barks.`);
  }
}

var d = new Dog('Mitzie');
d.speak();// 'Mitzie barks.'
</code></pre>
<p>如果子类中定义了构造函数，那么它必须先调用 <code>super()</code> 才能使用 <code>this</code> 。</p>

              </div>
              <div class="toc-container">
                <ul class="markdownIt-TOC">
<li><a href="#1-%E5%9F%BA%E7%A1%80%E6%A6%82%E5%BF%B5">1 基础概念</a>
<ul>
<li><a href="#11-%E7%AE%80%E4%BB%8B">1.1 简介</a></li>
<li><a href="#12-%E7%B1%BB%E5%9E%8B">1.2 类型</a>
<ul>
<li><a href="#121-%E6%95%B0%E5%80%BC">1.2.1 数值</a></li>
<li><a href="#122-%E5%AD%97%E7%AC%A6%E4%B8%B2">1.2.2 字符串</a></li>
<li><a href="#123-%E5%85%B6%E4%BB%96%E7%B1%BB%E5%9E%8B">1.2.3 其他类型</a></li>
</ul>
</li>
<li><a href="#13-%E5%8F%98%E9%87%8F">1.3 变量</a></li>
<li><a href="#14-%E8%BF%90%E7%AE%97%E7%AC%A6">1.4 运算符</a></li>
<li><a href="#15-%E6%8E%A7%E5%88%B6%E7%BB%93%E6%9E%84">1.5 控制结构</a></li>
<li><a href="#16-%E5%AF%B9%E8%B1%A1">1.6 对象</a></li>
<li><a href="#17-%E6%95%B0%E7%BB%84">1.7 数组</a></li>
<li><a href="#18-%E5%87%BD%E6%95%B0">1.8 函数</a></li>
<li><a href="#19-%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AF%B9%E8%B1%A1">1.9 自定义对象</a></li>
<li><a href="#110-%E5%86%85%E9%83%A8%E5%87%BD%E6%95%B0">1.10 内部函数</a></li>
<li><a href="#111-%E9%97%AD%E5%8C%85">1.11 闭包</a></li>
</ul>
</li>
<li><a href="#2-es6%E7%89%B9%E6%80%A7">2 ES6特性</a>
<ul>
<li><a href="#21-%E7%AE%AD%E5%A4%B4%E5%87%BD%E6%95%B0">2.1 箭头函数</a></li>
<li><a href="#22-class">2.2 class</a></li>
</ul>
</li>
</ul>

              </div>
            </div>
          </article>
        </div>

        

        
          
            <link rel="stylesheet" href="https://unpkg.com/gitalk/dist/gitalk.css">
<script src="https://unpkg.com/gitalk/dist/gitalk.min.js"></script>

<div id="gitalk-container"></div>

<script>

  var gitalk = new Gitalk({
    clientID: '447058976c8bd4fe04f6',
    clientSecret: 'b4a632203857b02425883cbf9b5bea3d5a2f86b0',
    repo: 'timegarage.github.io',
    owner: 'TimeGarage',
    admin: ['TimeGarage'],
    id: (location.pathname).substring(0, 49),      // Ensure uniqueness and length less than 50
    distractionFreeMode: false  // Facebook-like distraction free mode
  })

  gitalk.render('gitalk-container')

</script>

          

          
        

        <div class="site-footer">
  Copyright © 2020 TimeGarage Inc. | 
  <a class="rss" href="https://www.timegarage.works/atom.xml" target="_blank">RSS</a>
</div>

<script>
  hljs.initHighlightingOnLoad()

  let mainNavLinks = document.querySelectorAll(".markdownIt-TOC a");

  // This should probably be throttled.
  // Especially because it triggers during smooth scrolling.
  // https://lodash.com/docs/4.17.10#throttle
  // You could do like...
  // window.addEventListener("scroll", () => {
  //    _.throttle(doThatStuff, 100);
  // });
  // Only not doing it here to keep this Pen dependency-free.

  window.addEventListener("scroll", event => {
    let fromTop = window.scrollY;

    mainNavLinks.forEach((link, index) => {
      let section = document.getElementById(decodeURI(link.hash).substring(1));
      let nextSection = null
      if (mainNavLinks[index + 1]) {
        nextSection = document.getElementById(decodeURI(mainNavLinks[index + 1].hash).substring(1));
      }
      if (section.offsetTop <= fromTop) {
        if (nextSection) {
          if (nextSection.offsetTop > fromTop) {
            link.classList.add("current");
          } else {
            link.classList.remove("current");    
          }
        } else {
          link.classList.add("current");
        }
      } else {
        link.classList.remove("current");
      }
    });
  });

</script>

      </div>
    </div>
  </body>
</html>
